﻿// Copyright (c) 2012 Mokah Nnamezie & Andrew Cooper
// Neo4Net graph database - http://neo4net.codeplex.com
//
// This file is part of Neo4Net - a .Net port of the Neo4j graph database
//
// Neo4Net is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace Neo4Net.Helpers
{
    using System;
    using System.Reflection;

    public static class Exceptions
    {
        private const string unexpectedMessage = "Unexpected Exception";

        private static readonly FieldInfo innerExceptionField = typeof(Exception).GetField("_innerException", BindingFlags.Instance | BindingFlags.NonPublic);

        public static void SetInnerException(this Exception exception, Exception innerException)
        {
            innerExceptionField.SetValue(exception, innerException);
        }

        public static T WithInnerException<T>(this T exception, Exception cause)
            where T : Exception
        {
            try
            {
                exception.SetInnerException(cause);
            }
            catch (Exception ex)
            {
                // OK, we did our best, guess there will be no inner exception
            }
            return exception;
        }

        public static Exception Launder(Exception exception)
        {
            return exception.Launder(unexpectedMessage);
        }

        public static Exception Launder(this Exception exception, string messageForUnexpected)
        {
            return exception.Launder<Exception>(messageForUnexpected);
        }

        public static T Launder<T>(this Exception exception)
            where T : Exception
        {
            return exception.Launder<T>(unexpectedMessage);
        }

        public static T Launder<T>(this Exception exception, String messageForUnexpected)
            where T : Exception
        {
            if (exception is T)
            {
                return (T)exception;
            }
            else if (exception is TargetInvocationException)
            {
                return (exception as TargetInvocationException).InnerException.Launder<T>(messageForUnexpected);
            }
            else
            {
                throw new Exception(messageForUnexpected, exception);
            }
        }
    }
}
